package com.flashflexpro.flexdirector.agent.console{
    import com.flashflexpro.flexdirector.adapter.AutoEnv;
    import com.flashflexpro.flexdirector.adapter.CodecHelper;
    import com.flashflexpro.flexdirector.agent.FlexDirectorAgent;
    import com.flashflexpro.flexdirector.agent.scriptrunner.ScriptRunner;
    import com.flashflexpro.flexdirector.agent.view.ViewHandler;
    import com.flashflexpro.flexdirector.conn.RemoteCallingEvent;


    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.geom.Rectangle;
    import flash.utils.ByteArray;
    import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;
    import flash.utils.getQualifiedSuperclassName;

    import mx.automation.Automation;
    import mx.automation.AutomationID;
    import mx.automation.IAutomationClass;
    import mx.automation.IAutomationObject;
    import mx.automation.events.AutomationRecordEvent;
    import mx.core.UIComponent;
    import mx.managers.SystemManager;

    public class Console{

        private static var _inst:Console;

        public static function get inst():Console{
            if( _inst == null ){
                throw new Error( "this should be inited earliest!" );
            }
            return _inst;
        }

        public function Console( appInstId:String, ui:SystemManager, serverIp:String, serverPort:int ){
            if( _inst != null ){
                throw new Error( "I'm single!" );
            }
            _inst = this;
            _consoleConn = new ConnectionToConsole( ui );
            _consoleConn.addEventListener( RemoteCallingEvent.TYPE, onConsoleCall );
            _consoleConn.connectTo( appInstId, serverIp, serverPort );
        }


        private var _recordNow:int;

        private function onConsoleCall( connEv:RemoteCallingEvent ):void{
            if( connEv.methodName == RemoteCallingEvent._c2aRunExeSwf ){
                ScriptRunner.loadAndRunBinary( connEv.args[ 0 ] as ByteArray, connEv.args[ 1 ] as String );
            } else if( connEv.methodName == RemoteCallingEvent._c2aRunExeBuiltIn ){
                ScriptRunner.loadAndRunBuiltIn( connEv.args[ 0 ] as String );
            } else if( connEv.methodName == RemoteCallingEvent._c2aRecording ){
                FlexDirectorAgent.inst.removeEventListener( AutomationRecordEvent.RECORD, onRecordingEvent );
                if( connEv.args[ 0 ] ){
                    FlexDirectorAgent.inst.addEventListener( AutomationRecordEvent.RECORD, onRecordingEvent );
                    _recordNow = new Date().getTime();
                    FlexDirectorAgent.startRecording();
                }
                else{
                    FlexDirectorAgent.stopRecording()
                }
            } else if( connEv.methodName == RemoteCallingEvent._c2aChecking ){
                if( connEv.args[ 0 ] ){
                    _recordNow = new Date().getTime();
                    ViewHandler.inst.startLocating();
                }
                else{
                    ViewHandler.inst.stopLocating();
                }
            } else if( connEv.methodName == RemoteCallingEvent._c2aEnv ){
                Automation.automationManager.automationEnvironment = new AutoEnv( connEv.args[ 0 ] );
            } else if( connEv.methodName == RemoteCallingEvent.DISCONNECTED ){
                _consoleConn = null;
            }
        }

        private function onRecordingEvent( event:AutomationRecordEvent ):void{
            var ui:UIComponent = event.automationObject as UIComponent;
            var mainAndSuperClassNames:Array = [ getQualifiedClassName( event.automationObject )].concat( getSuperClassNames( event.automationObject ) );
            var autoId:AutomationID = Automation.automationManager.createID( event.automationObject );
            var autoIdStr:String = autoId.toString();
            var now:int = new Date().getTime();
            _consoleConn.callPeer( RemoteCallingEvent._a2cOnActionEvent, autoIdStr, event.name, event.args,
                    getBitMap( event.automationObject ), now - _recordNow, mainAndSuperClassNames, ui.width, ui.height );
            _recordNow = now;
        }

        private var _consoleConn:ConnectionToConsole;

        public function onCheckLocated( autoObj:IAutomationObject ):void{
            var ui:UIComponent = autoObj as UIComponent;
            var autoIdStr:String = Automation.automationManager.createID( autoObj ).toString();

            var a:IAutomationClass = Automation.automationManager.automationEnvironment
                    .getAutomationClassByInstance( autoObj );
            var pds:Array = a.getPropertyDescriptors( null, true, false );
            CONFIG::debugging{
                if( pds.length == 0 ){
                    throw new Error( this + ".onCheckLocated pds.length is 0" );
                }
            }
            var checkingProperties:Array = CodecHelper.encodeProperties( autoObj,
                    pds, null );
            CONFIG::debugging{
                if( checkingProperties.length == 0 ){
                    throw new Error( this + ".onCheckLocated properties.length is 0" );
                }
            }
            var now:int = new Date().getTime();

            var mainAndSuperClassNames:Array = [ getQualifiedClassName( autoObj )].concat( getSuperClassNames( autoObj ) );
            _consoleConn.callPeer( RemoteCallingEvent._a2cOnActionEvent,
                    autoIdStr, null, checkingProperties,
                    getBitMap( autoObj ), now - _recordNow, mainAndSuperClassNames, ui.width, ui.height );
            _recordNow = now;
        }

        public static function getSuperClassNames( current:* ):Array{
            var rt:Array = [ ];
            while( current != null ){
                var currentClassName:String = getQualifiedSuperclassName( current );
                if( current != null ){
                    rt.push( currentClassName );
                    if( currentClassName == "mx.core::UIComponent" ){
                        break;
                    }
                    currentClassName = getQualifiedSuperclassName( current );
                    if( currentClassName == null ){
                        break;
                    }
                    current = getDefinitionByName( currentClassName );
                }
            }
            return rt;
        }

        private function getBitMap( autoObj:IAutomationObject ):ByteArray{
            var ui:DisplayObject = autoObj as DisplayObject;
            if( ui == null ){
                return null;
            }
            try{
                var btmp:BitmapData = new BitmapData( ui.width, ui.height );
                btmp.draw( ui );
                var rt:ByteArray = btmp.getPixels( new Rectangle( 0, 0, btmp.width, btmp.height ) );
                rt.compress();
                return rt;
            }
            catch( e:Error ){
            }
            return null;
        }

        public function procedureStart( procedureName:String ):void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cProcedureStart, procedureName );
        }

        public function procedureSuceeded( procedureName:String ):void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cProcedureSuceeded, procedureName );
        }

        public function procedureFailed( procedureName:String ):void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cProcedureFailed, procedureName );
        }

        //>>>
        public function stepRun( procedureName:String, stepNum:int ):void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cStepWaiting, procedureName, stepNum );
        }

        public function stepError( procedureName:String, stepNum:int, error:String ):void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cStepError, procedureName, stepNum, error );
        }

        public function stepFinish( procedureName:String, stepNum:int, successfully:Boolean ):void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cStepFinish, procedureName, stepNum, successfully );
        }

        //<<<

        public function exit():void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cExit );
        }

        public function begin():void{
            _consoleConn.callPeer( RemoteCallingEvent._a2cBegin );
        }
    }
}